﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;

namespace TCP.SocketHelper
{
    /// <summary>
    /// Socket的客户端
    /// </summary>
    public class SocketClient : BaseTCPSocket
    {
        #region --Protected Field

        /// <summary>
        /// 消息头缓冲
        /// </summary>
        protected byte[] _BufferHead;

        /// <summary>
        /// 消息体一次缓冲
        /// </summary>
        protected byte[] _BufferBody;

        /// <summary>
        /// 接收消息体的内存流
        /// </summary>
        protected MemoryStream _MSReceiveMsgBody = new MemoryStream();

        /// <summary>
        /// 发送消息体的内存流
        /// </summary>
        protected MemoryStream _MSSendMsgBody = new MemoryStream();

        /// <summary>
        /// 消息头的缓冲区信息
        /// </summary>
        protected SocketBufferInfo _BufferHeadInfo = new SocketBufferInfo()
        {
            BufferSize = SocketSetting.SOCKET_MSG_HEAD_LENGTH,
            ReadSize = 0,
        };

        /// <summary>
        /// 消息体的单次缓冲区信息
        /// </summary>
        protected SocketBufferInfo _BufferBodyInfo = new SocketBufferInfo()
        {
            BufferSize = SocketSetting.SOCKET_MSG_BODY_MAX,
            ReadSize = 0,
        };

        /// <summary>
        /// 消息体的单次缓冲区信息
        /// </summary>
        protected SocketBufferInfo _BufferMsgInfo = new SocketBufferInfo();

        #endregion

        #region --Public Property

        #endregion

        #region --Public Callback Event

        /// <summary>
        /// 连接成功的回调
        /// </summary>
        public Action OnConnectSuccrssAction = null;

        /// <summary>
        /// 连接失败的回调
        /// </summary>
        public Action OnConnectFailAction = null;

        /// <summary>
        /// 处理消息类型检测的回调
        /// </summary>
        public Action<BaseTCPSocket> CheckMsgCmdAction = null;

        /// <summary>
        /// 指令ID绑定
        /// </summary>
        public Action<int> OnMsgCmdAction = null;

        /// <summary>
        /// 收到名字确认的回调
        /// </summary>
        public Action<string,SocketClient> OnConrimNameAction = null;

        /// <summary>
        /// 校验消息头
        /// </summary>
        public Func<byte[], bool> CheckHeadFunc = null;


        #endregion

        public SocketClient()
        {

        }

        public SocketClient(string ip, int port):this()
        {
            _TargetIP = ip;
            _TargetPort = port;
        }

        public override void SetSocketClient(Socket socket)
        {
            base.SetSocketClient(socket);
            OnConnectionSuccess();
        }

        /// <summary>
        /// 开始连接
        /// </summary>
        public override void StartConnect()
        {
            try
            {
                IPAddress[] hostAddresses = Dns.GetHostAddresses(_TargetIP);
                if (hostAddresses[0].AddressFamily == AddressFamily.InterNetworkV6)
                {
                    _Socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
                }
                else
                {
                    _Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                }

                //使用异步等待连接结果
                IAsyncResult asyncResult = _Socket.BeginConnect(hostAddresses[0], _TargetPort, new AsyncCallback(OnConnect), _Socket);
                //注册连接超时
                ThreadPool.RegisterWaitForSingleObject(asyncResult.AsyncWaitHandle, new WaitOrTimerCallback(BeginConnentTimeOut), _Socket, SocketSetting.TCP_CONNECT_TIMEOUT, true);
            }
            catch (Exception ex)
            {
                NetDrop(GetType() + ".StartConnect:" + ex.Message);
                OnConnectFailAction?.Invoke();
            }
        }

        /// <summary>
        /// 开始进行连接之后的异步结果
        /// </summary>
        /// <param name="result"></param>
        protected virtual void OnConnect(IAsyncResult result)
        {
            if ((Socket)result.AsyncState == null)
            {
                NetDrop($"{GetType()}.OnConnect:result.AsyncState is null.");
                OnConnectFailAction?.Invoke();
                return;
            }
            try
            {
                _Socket.EndConnect(result);
                _IsConnect = true;
                OnConnectionSuccess();
            }
            catch (Exception ex)
            {
                NetDrop($"{GetType()}.OnConnect:{ex.Message}");
                OnConnectFailAction?.Invoke();
            }
        }

        /// <summary>
        /// 连接成功后开始设置各缓冲区的基础内容，同时打开消息头接收
        /// </summary>
        protected virtual void OnConnectionSuccess()
        {
            Log($"{GetType()}.OnConnectionSuccess");
            OnConnectSuccrssAction?.Invoke();
            _BufferHead = new byte[SocketSetting.SOCKET_MSG_HEAD_LENGTH];
            _BufferBody = new byte[SocketSetting.SOCKET_MSG_BODY_MAX];
            ReceiveHead();
        }

        /// <summary>
        /// 连接超时的异步结果
        /// </summary>
        /// <param name="state"></param>
        /// <param name="isTimedOut"></param>
        protected virtual void BeginConnentTimeOut(object state, bool isTimedOut)
        {
            if (isTimedOut)
            {
                NetDrop($"{GetType()}.BeginConnentTimeOut:TimeOut");
                OnConnectFailAction?.Invoke();
            }
        }

        /// <summary>
        /// 开始接收数据头
        /// </summary>
        protected virtual void ReceiveHead()
        {
            if (!IsConnect)
            {
                NetDrop($"{GetType()}.ReceiveHead:connect is {IsConnect}");
                return;
            }
            try
            {
                Log($"{GetType()}.ReceiveHead:BeginReceiveHead", 0);

                //重置消息头缓冲区信息
                _BufferHeadInfo.ReadSize = 0;
                _Socket.BeginReceive(_BufferHead, 0, _BufferHeadInfo.BufferSize, SocketFlags.None, new AsyncCallback(OnReceiveHead), _Socket);
            }
            catch (Exception ex)
            {
                NetDrop($"{GetType()}.ReceiveHead:{ex.Message}");
            }
        }

        /// <summary>
        /// 接收消息头的异步结果
        /// </summary>
        /// <param name="result"></param>
        protected virtual void OnReceiveHead(IAsyncResult result)
        {
            if (!IsConnect)
            {
                NetDrop($"{GetType()}.OnReceiveHead:connect is {IsConnect}");
                return;
            }
            try
            {
                int receive_head_len = _Socket.EndReceive(result);
                _BufferHeadInfo.ReadSize = _BufferHeadInfo.ReadSize + receive_head_len;
            }
            catch (Exception ex)
            {
                NetDrop($"{GetType()}.OnReceiveHead:{ex.Message}");
                return;
            }

            if (_BufferHeadInfo.ReadSize < _BufferHeadInfo.BufferSize)
            {
                try
                {
                    //读入的数据量未满时，继续读入剩余的消息头（调用异步为 OnReceiveHead）
                    _Socket.BeginReceive(_BufferHead, _BufferHeadInfo.ReadSize, _BufferHeadInfo.BufferSize - _BufferHeadInfo.ReadSize, SocketFlags.None, new AsyncCallback(OnReceiveHead), _Socket);
                }
                catch (Exception ex)
                {
                    NetDrop($"{GetType()}.OnReceiveHead:{ex.Message}");
                }
            }
            else
            {
                if (_CheckHead())
                {
                    _CheckMsgCmd();
                }
                else
                {
                    Log($"{GetType()}.OnReceiveHead: _CheckHead fail.", 0);
                }
            }
        }

        /// <summary>
        /// 校验消息头
        /// </summary>
        /// <param name="data"></param>
        protected virtual bool _CheckHead()
        {
            if (CheckHeadFunc != null)
            {
                return CheckHeadFunc(_BufferHead);
            }

            short flag = BitConverter.ToInt16(_BufferHead, 0);
            bool isFlag = flag == SocketSetting.SOCKET_MSG_HEAD_FLAG;

            return isFlag;
        }

        /// <summary>
        /// 检验消息命令
        /// </summary>
        /// <returns></returns>
        protected virtual void _CheckMsgCmd()
        {
            try
            {
                if (CheckMsgCmdAction != null)
                {
                    CheckMsgCmdAction(this);
                }
                else
                {
                    SocketCommand cmd = (SocketCommand)BitConverter.ToInt16(_BufferHead, 2);
                    switch (cmd)
                    {
                        default:
                            Log($"{GetType()}.OnReceiveHead: get cmd not define: {cmd}", 2);
                            break;
                        case SocketCommand.NORMAL:
                            _BufferMsgInfo.BufferSize = BitConverter.ToInt32(_BufferHead, 4);
                            _BufferMsgInfo.ReadSize = 0;
                            _MSReceiveMsgBody.Position = 0;
                            _Socket.BeginReceive(_BufferBody, 0, _BufferBodyInfo.BufferSize, SocketFlags.None, new AsyncCallback(OnReceive), _Socket);
                            break;
                        case SocketCommand.CALLBACK:
                            OnMsgCmdAction?.Invoke(BitConverter.ToInt32(_BufferHead, 4));
                            ReceiveHead();
                            break;
                        case SocketCommand.TICK_CHECK:
                            _OnTickCheckReceive(BitConverter.ToInt32(_BufferHead, 4));
                            break;
                        case SocketCommand.CONFIRM_NAME:
                            _BufferMsgInfo.BufferSize = BitConverter.ToInt32(_BufferHead, 4);
                            _BufferMsgInfo.ReadSize = 0;
                            _MSReceiveMsgBody.Position = 0;
                            _Socket.BeginReceive(_BufferBody, 0, _BufferBodyInfo.BufferSize, SocketFlags.None, new AsyncCallback(OnReceive), _Socket);
                            break;
                    }
                }
            }
            catch (Exception ex)
            {
                Log($"{GetType()}._CheckMsgCmd:{ex.Message}", 1);
            }
        }

        /// <summary>
        /// 接收消息体
        /// </summary>
        /// <param name="result"></param>
        protected virtual void OnReceive(IAsyncResult result)
        {
            if (!IsConnect)
            {
                NetDrop($"{GetType()}.OnReceive:connect is {IsConnect}");
                return;
            }
            try
            {
                int receive_body_len = _Socket.EndReceive(result);
                _BufferMsgInfo.ReadSize = _BufferMsgInfo.ReadSize + receive_body_len;
                _MSReceiveMsgBody.Write(_BufferBody, 0, receive_body_len);
                _BufferBodyInfo.ReadSize = 0;
            }
            catch (Exception ex)
            {
                NetDrop($"{GetType()}.OnReceive:{ex.Message}");
                return;
            }

            if (_BufferMsgInfo.ReadSize < _BufferMsgInfo.BufferSize)
            {
                try
                {
                    _Socket.BeginReceive(_BufferBody, _BufferBodyInfo.ReadSize, _BufferBodyInfo.BufferSize, SocketFlags.None, new AsyncCallback(OnReceive), _Socket);
                }
                catch (Exception e)
                {
                    NetDrop($"{GetType()}.OnReceive:Receive message failed,{e.Message}");
                }
            }
            else
            {
                // 消息体读取完毕，开始比处理消息体
                byte[] data = new byte[_BufferMsgInfo.BufferSize];
                _MSReceiveMsgBody.Position = 0;
                _MSReceiveMsgBody.Read(data, 0, _BufferMsgInfo.BufferSize);
                SocketCommand cmd = (SocketCommand)BitConverter.ToInt16(_BufferHead, 2);
                switch (cmd)
                {
                    default :
                        Log($"{GetType()}.OnReceive: head cmd not define: {cmd},use SocketCommand.Normal mode.", 2);
                        ReceiveMsgAction?.Invoke(data);
                        break;
                    case SocketCommand.NORMAL:
                        ReceiveMsgAction?.Invoke(data);
                        break;
                    case SocketCommand.CONFIRM_NAME:
                        OnConrimNameAction?.Invoke(Encoding.UTF8.GetString(data),this);
                        break;
                }                
                ReceiveHead();
            }
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="data"></param>
        protected virtual void _SendMessage(byte[] data)
        {
            try
            {
                int length = data.Length;
                _Socket.BeginSend(data, 0, length, SocketFlags.None, new AsyncCallback(OnSend), null);
            }
            catch (Exception ex)
            {
                NetDrop($"{GetType()}.SendMessage:{ex.Message}");
            }
        }

        /// <summary>
        /// 发送消息完毕的异步
        /// </summary>
        /// <param name="result"></param>
        protected virtual void OnSend(IAsyncResult result)
        {
            if (!IsConnect)
            {
                NetDrop($"{GetType()}.OnSend:connect is {IsConnect}");
                return;
            }
            try
            {
                if (_Socket.EndSend(result) == 0)
                {
                    NetDrop($"{GetType()}.OnSend:Failed to send msg.");
                }


            }
            catch (Exception ex)
            {
                NetDrop($"{GetType()}.OnSend:{ex.Message}");
            }
        }

        /// <summary>
        /// 接收到心跳回调时
        /// </summary>
        /// <param name="tick_data"></param>
        protected virtual void _OnTickCheckReceive(int tick_data)
        {
            tick_data++;
            SendMessage(null, SocketCommand.TICK_CHECK, tick_data);
            ReceiveHead();
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="data">消息体</param>
        /// <param name="cmd">消息类型</param>
        /// <param name="content">消息其他参数</param>
        public virtual void SendMessage(byte[] data,SocketCommand cmd = SocketCommand.NORMAL,params object[] content)
        {
            _MSSendMsgBody.Position = 0;
            _MSSendMsgBody.Write(SocketSetting.SOCKET_HEAD_FLAG, 0, SocketSetting.SOCKET_HEAD_FLAG.Length);
            _MSSendMsgBody.Write(BitConverter.GetBytes(((short)cmd)), 0, 2);
            switch (cmd)
            {
                default:
                    Log($"{GetType()}.SendMessage:try to send {cmd} is not define.Use SocketCommand.NORMAL mode.");
                    if (data == null || data.Length <= 0)
                    {
                        Log($"{GetType()}.SendMessage data is null.", 2);
                        return;
                    }
                    _MSSendMsgBody.Write(BitConverter.GetBytes(data.Length), 0, 4);
                    _MSSendMsgBody.Write(data, 0, data.Length);
                    break;
                case SocketCommand.NORMAL:
                    if (data == null || data.Length <= 0)
                    {
                        Log($"{GetType()}.SendMessage data is null.", 2);
                        return;
                    }
                    _MSSendMsgBody.Write(BitConverter.GetBytes(data.Length), 0, 4);
                    _MSSendMsgBody.Write(data, 0, data.Length);
                    break;
                case SocketCommand.CALLBACK:
                    _MSSendMsgBody.Write(BitConverter.GetBytes((int)content[0]), 0, 4);
                    break;
                case SocketCommand.TICK_CHECK:
                    _MSSendMsgBody.Write(BitConverter.GetBytes((int)content[0]), 0, 4);
                    break;

                case SocketCommand.CONFIRM_NAME:
                    _MSSendMsgBody.Write(BitConverter.GetBytes(data.Length), 0, 4);
                    _MSSendMsgBody.Write(data, 0, data.Length);
                    break;
            }
            byte[] send_data = new byte[_MSSendMsgBody.Position];
            _MSSendMsgBody.Position = 0;
            _MSSendMsgBody.Read(send_data, 0, send_data.Length);
            _SendMessage(send_data);
        }

        /// <summary>
        /// 发送名称确认消息
        /// </summary>
        /// <param name="name"></param>
        public virtual void SendName(string name)
        {
            byte[] data = Encoding.UTF8.GetBytes(name);
            SendMessage(data, SocketCommand.CONFIRM_NAME);
        }
    }
}
